/*
 * auth.cpp
 *
 *  Created on: Dec 13, 2013
 *      Author: killerg
 */

#include "auth_deps.h"
#include "auth.h"
#include "auth_cfg.h"
#include "auth_mgr.h"
#include "logonsess.h"
#include "auth_console.h"

#include "share/clock.h"
#include "share/cfg_file.h"
#include "share/la_msg.pb.h"
#include "share/net_s.h"
#include "share/util.h"
#include "share/console2.h"

#include "version.h"

#include <stdlib.h>
#include <execinfo.h>

auth_t g_auth;

static int log_cb(int mask, const char* fmt, ...) {
    va_list arg;
    va_start(arg, fmt);
    int n = g_console->vlog(mask, fmt, arg);
    va_end(arg);
    return n;
}
static int vlog_cb(int mask, const char* fmt, va_list args) {
    int n = g_console->vlog(mask, fmt, args);
    return n;
}


static void handle_ctrl_c(int signal) {
    g_auth.svcdata->shut = true;
}

int mem_log(const char* fmt, va_list arg) {
	int idx = g_auth.mem->cur_idx;
	g_auth.mem->cur_idx = (g_auth.mem->cur_idx + 1) % LOGNUM;

	g_auth.mem->logs[idx].time = g_clock->tt_now;
	int wroten = vsnprintf(g_auth.mem->logs[idx].text, LOGITEMLEN-1, fmt, arg);
	g_auth.mem->logs[idx].text[wroten] = 0;
	return wroten;
}

int mem_log(const char* fmt, ...) {
    va_list arg;
    va_start(arg, fmt);
    int n = mem_log(fmt, arg);
    va_end(arg);
    return n;
}


bool auth_t::init(SvcData* svcdata_) {
	mem = (mem_t*)shm_get(SHMPATH, sizeof(mem_t));
	if (mem) {
		int fd = open(fmt_cstr("%s/log/auth_memlog_%d", get_deploy(), mem->pid), O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP);
		if (fd < 0) {
			abort();
		}

		char buff[100];
		time_t now = time(NULL);
		strftime(buff,100,"%Y-%m-%d_%H:%M:%S",localtime(&mem->create_ts));

		const char* str = fmt_cstr("pid:%d time:%s\n", mem->pid, buff);
		int towrite = strlen(str);
		int wrote = write(fd, str, towrite);
		if (wrote != towrite) {
			perror("write");
			abort();
		}

		for (int i=0; i<LOGNUM; ++i) {
			int idx = (mem->cur_idx + i) % LOGNUM;
			if (mem->logs[idx].time == 0 || strlen(mem->logs[idx].text) == 0)
				continue;

			char buff[100];
			time_t now = time(NULL);
			strftime(buff,100,"%Y-%m-%d_%H:%M:%S",localtime(&mem->logs[idx].time));
			const char* str = fmt_cstr("%s:%s\n", buff, mem->logs[idx].text);
			int towrite = strlen(str);
			assert(write(fd, str, towrite) == towrite);
		}
		close(fd);
	}

	if (!mem)
		mem = (mem_t*)shm_new(SHMPATH, sizeof(mem_t));

	if (!mem)
		abort();

	on_signal(SIGINT, handle_ctrl_c);
	sig_ignore(SIGPIPE);

	svcdata = svcdata_;
    cfg = new auth_cfg_t;
    clock = new my_clock_t;
    authmgr = new auth_mgr_t;
    logonsess_mgr = new logonsess_mgr_t;
    console = new auth_console_t;
    db_log = new db_mgr_t;

    std::string logname = fmt_cstr("auth_%s", svcdata->prefix);
    if (!console->init_log(logname.c_str())) {
    	//printf("g_console log init failed\r\n");
        return false;
    }

    util_logcb = log_cb;
    util_vlogcb = vlog_cb;

    UTIL_LOG(I, "branch:%s, commit:%s", GIT_BRANCH, GIT_COMMIT_HASH);

    UTIL_LOG(I, "start init");

    if (!cfg->init(svcdata->prefix)) {
        UTIL_LOG(E, "cfg init failed");
        return false;
    }

    if (!console->init()) {
        UTIL_LOG(E, "console init net init failed");
        return false;
    }

    if (!clock->init(1000)) {
        UTIL_LOG(E, "clock init failed");
        return false;
    }

    if (!db_log->init(cfg->LogDbHost, "", "", cfg->LogDbBucket)) {
        UTIL_LOG(E, "db_log init failed");
        return false;
    }

    if (!authmgr->init()) {
        UTIL_LOG(E, "authmgr init failed");
        return false;
    }

    if (!logonsess_mgr->init()) {
        UTIL_LOG(E, "logonsess init failed");
        return false;
    }

    console->watch_n("run_sec", [this]()->int{ return int(clock->eclipse); });
    console->watch_n("auth_db_geting", [this]()->int{ return authmgr->db.pending_get; });
    console->watch_n("auth_db_seting", [this]()->int{ return authmgr->db.pending_set; });
    console->watch_n("auth_db_adding", [this]()->int{ return authmgr->db.pending_add; });
    console->watch_n("auth_pending", [this]()->int{ return authmgr->dealing.size(); });
    console->watch_n("httpop_num", [this]()->int{ return authmgr->htpc.pending_num(); });
    console->watch_n("authed_ps", [this]()->int{ return authmgr->authed_ps; });
    console->watch_n("cached_account", [this]()->int{ return authmgr->cache.size(); });
    console->watch_n("active_cache", [this]()->int{ return authmgr->mcactive.size(); });

#define WATCH_TIMER(name) console->watch_f(#name "frame_busy", [this]()->double{ return cpu_time; }, [this](){ return 1.0f; })
    WATCH_TIMER(sessmgr);
#undef WATCH_TIMER


    UTIL_LOG(I, "end init");

	memset(mem, 0, sizeof(*mem));
	mem->pid = getpid();
	mem->create_ts = time(NULL);
	mem->cur_idx = 0;

    return true;
}
void auth_t::destroy() {
    UTIL_LOG(I, "start destroy");

    logonsess_mgr->destroy();
    authmgr->destroy();
    db_log->destroy();
    clock->destroy();
    cfg->destroy();

    UTIL_LOG(I, "end destroy");

    console->destroy();

    safe_del(logonsess_mgr);
    safe_del(authmgr);
    safe_del(clock);
    safe_del(cfg);
    safe_del(console);

    shm_free(SHMPATH, sizeof(mem_t), mem);
}
void auth_t::update() {
    UTIL_LOG(I, "start loop");

    while (!svcdata->shut) {
        clock->begin_update();

        db_log->update();
        mem_log("db_log | authmgr");
        authmgr->update();
        mem_log("authmgr | logonsess_mgr");
        logonsess_mgr->update(clock->delta);
        mem_log("logonsess_mgr | console");
        console->update();

        clock->end_update();
        clock->wait_frame();

        cpu_time += clock->frame_busy;
        if (clock->sec_jump) {
        	authmgr->authed_ps = 0;
        	cpu_time = 0.0f;
        }
        svcdata->refresh = clock->tt_now;
    }

    UTIL_LOG(I, "end loop");
}
